/*
 * $Id: CompositeLoggerConfigurationProvider.java 128 2013-01-22 06:29:13Z vladimirow@mail.ru $
 * $HeadURL: http://ultralog.googlecode.com/svn/ultralog/trunk/src/java/com/google/code/ultralog/configuration/CompositeLoggerConfigurationProvider.java $
 *
 * This file is a part of ultralog project (http://code.google.com/p/ultralog/).
 * Released under New BSD License.
 */
package com.google.code.ultralog.configuration;

import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;

import com.google.code.ultralog.utils.CharacterSequence;
import com.google.code.ultralog.writer.LogRecordWriter;
import com.google.code.ultralog.writer.LogRecordWriter.LogRecord;

/**
 * Combines several logger configuration providers together.
 *
 * @author Mikhail Vladimirov
 */
public class CompositeLoggerConfigurationProvider
    implements LoggerConfigurationProvider
{
    private final LoggerConfigurationProvider [] providers;

    /**
     * Create new instance of composite configuration provider that combines
     * given logger configuration providers.
     *
     * @param providers logger configuration providers to be combined
     */
    public CompositeLoggerConfigurationProvider (
        LoggerConfigurationProvider ... providers)
    {
        if (providers == null)
            throw new IllegalArgumentException ("Providers is null");

        int nullIndex = Arrays.asList (providers).indexOf (null);
        if (nullIndex >= 0)
            throw new IllegalArgumentException (
                "Providers [" + nullIndex + "] is null");

        this.providers = providers.clone ();
    }

    /**
     * {@inheritDoc}
     */
    @Override
    public LoggerConfiguration getConfiguration (String loggerName)
    {
        if (loggerName == null)
            throw new IllegalArgumentException ("Logger name is null");

        int count = providers.length;
        LoggerConfiguration [] configurations = new LoggerConfiguration [count];

        for (int i = 0; i < count; i++)
            configurations [i] = providers [i].getConfiguration (loggerName);

        return new MyLoggerConfiguration (configurations);
    }

    private static class MyLoggerConfiguration implements LoggerConfiguration
    {
        private final static ThreadLocal <MyLogRecord> LOG_RECORD =
            new ThreadLocal <MyLogRecord> ();

        private final LoggerConfiguration [] configurations;
        private final LogRecordWriter logRecordWriter =
            new MyLogRecordWriter ();

        public MyLoggerConfiguration (
            LoggerConfiguration ... configurations)
        {
            if (configurations == null)
                throw new IllegalArgumentException ("Configurations is null");

            int nullIndex = Arrays.asList (configurations).indexOf (null);
            if (nullIndex >= 0)
                throw new IllegalArgumentException (
                    "Configurations [" + nullIndex + "] is null");

            this.configurations = configurations.clone ();
        }

        @Override
        public int getMinimumLoggingLevel ()
        {
            int result = Integer.MAX_VALUE;
            int count = configurations.length;

            for (int i = 0; i < count; i++)
                result = Math.min (
                    result, configurations [i].getMinimumLoggingLevel ());

            return result;
        }

        @Override
        public LogRecordWriter getLogRecordWriter ()
        {
            return logRecordWriter;
        }

        private class MyLogRecordWriter implements LogRecordWriter
        {
            @Override
            public LogRecord beginLogRecord (String loggerName, int level,
                String levelName)
            {
                if (loggerName == null)
                    throw new IllegalArgumentException ("Logger name is null");

                if (levelName == null)
                    throw new IllegalArgumentException ("Level name is null");

                MyLogRecord logRecord = LOG_RECORD.get ();
                if (logRecord == null)
                    logRecord = new MyLogRecord ();
                else
                    LOG_RECORD.set (null);

                int count = configurations.length;

                for (int i = 0; i < count; i++)
                {
                    LoggerConfiguration configuration = configurations [i];

                    if (level >= configuration.getMinimumLoggingLevel ())
                        logRecord.addLogRecord (
                            configuration.getLogRecordWriter ().
                                beginLogRecord (loggerName, level, levelName));
                }

                return logRecord;
            }
        }

        private static class MyLogRecord implements LogRecord
        {
            private final List <LogRecord> logRecords =
                new ArrayList <LogRecord> ();

            public void addLogRecord (LogRecord logRecord)
            {
                if (logRecord == null)
                    throw new IllegalArgumentException ("Log record is null");

                logRecords.add (logRecord);
            }

            @Override
            public void write (char ch)
            {
                for (int count = logRecords.size (), i = 0; i < count; i++)
                    logRecords.get (i).write (ch);
            }

            @Override
            public void write (char ch1, char ch2)
            {
                for (int count = logRecords.size (), i = 0; i < count; i++)
                    logRecords.get (i).write (ch1, ch2);
            }

            @Override
            public void write (char ch1, char ch2, char ch3)
            {
                for (int count = logRecords.size (), i = 0; i < count; i++)
                    logRecords.get (i).write (ch1, ch2, ch3);
            }

            @Override
            public void write (char ch1, char ch2, char ch3, char ch4)
            {
                for (int count = logRecords.size (), i = 0; i < count; i++)
                    logRecords.get (i).write (ch1, ch2, ch3, ch4);
            }

            @Override
            public void write (char[] buffer, int offset, int length)
            {
                if (buffer == null)
                    throw new IllegalArgumentException ("Buffer is null");

                if (offset < 0)
                    throw new IllegalArgumentException ("Offset (" + offset + ") < 0");

                if (length < 0)
                    throw new IllegalArgumentException ("Length (" + length + ") < 0");

                int bufferLength = buffer.length;
                if (offset + length > bufferLength)
                    throw new IllegalArgumentException (
                        "Offset (" + offset + ") + length (" + length +
                        ") > buffer length (" + bufferLength + ")");

                for (int count = logRecords.size (), i = 0; i < count; i++)
                    logRecords.get (i).write (buffer, offset, length);
            }

            @Override
            public void write (String string, int offset, int length)
            {
                if (string == null)
                    throw new IllegalArgumentException ("String is null");

                if (offset < 0)
                    throw new IllegalArgumentException ("Offset (" + offset + ") < 0");

                if (length < 0)
                    throw new IllegalArgumentException ("Length (" + length + ") < 0");

                int stringLength = string.length ();
                if (offset + length > stringLength)
                    throw new IllegalArgumentException (
                        "Offset (" + offset + ") + length (" + length +
                        ") > string length (" + stringLength + ")");

                for (int count = logRecords.size (), i = 0; i < count; i++)
                    logRecords.get (i).write (string, offset, length);
            }

            @Override
            public void write (CharSequence sequence, int offset, int length)
            {
                if (sequence == null)
                    throw new IllegalArgumentException ("Sequence is null");

                if (offset < 0)
                    throw new IllegalArgumentException ("Offset (" + offset + ") < 0");

                if (length < 0)
                    throw new IllegalArgumentException ("Length (" + length + ") < 0");

                int sequenceLength = sequence.length ();
                if (offset + length > sequenceLength)
                    throw new IllegalArgumentException (
                        "Offset (" + offset + ") + length (" + length +
                        ") > sequence length (" + sequenceLength + ")");

                for (int count = logRecords.size (), i = 0; i < count; i++)
                    logRecords.get (i).write (sequence, offset, length);
            }

            @Override
            public void write (CharacterSequence sequence, int offset,
                int length)
            {
                if (sequence == null)
                    throw new IllegalArgumentException ("Sequence is null");

                if (offset < 0)
                    throw new IllegalArgumentException ("Offset (" + offset + ") < 0");

                if (length < 0)
                    throw new IllegalArgumentException ("Length (" + length + ") < 0");

                int sequenceLength = sequence.getLength ();
                if (offset + length > sequenceLength)
                    throw new IllegalArgumentException (
                        "Offset (" + offset + ") + length (" + length +
                        ") > sequence length (" + sequenceLength + ")");

                for (int count = logRecords.size (), i = 0; i < count; i++)
                    logRecords.get (i).write (sequence, offset, length);
            }

            @Override
            public void flush ()
            {
                for (int count = logRecords.size (), i = 0; i < count; i++)
                    logRecords.get (i).flush ();
            }

            @Override
            public void write ()
            {
                for (int count = logRecords.size (), i = 0; i < count; i++)
                    logRecords.get (i).write ();

                logRecords.clear ();
                LOG_RECORD.set (this);
            }

            @Override
            public void cancel ()
            {
                logRecords.clear ();
                LOG_RECORD.set (this);
            }
        }
    }
}
